package com.dz51.core.proxy;

import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

import javax.sql.DataSource;

import org.apache.log4j.Logger;
import org.hibernate.HibernateException;
import org.hibernate.Session;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.orm.hibernate3.HibernateCallback;
import org.springframework.orm.hibernate3.HibernateTemplate;

import com.dz51.core.utils.SpringContextUtil;

@SuppressWarnings("unchecked")
public abstract class AbstractTemplateContainer implements TemplateContainer {

	private Logger logger = Logger.getLogger(AbstractTemplateContainer.class);

	private boolean checkThreadOpen = true;
	private boolean initDataSourceCheckOpen = true;

	protected int errorTimes = 5;
	protected int successTimes = 5;

	public List<String> warnEmails = new ArrayList<String>();
	public List<String> warnWeixins = new ArrayList<String>();

	public boolean isInitDataSourceCheckOpen() {
		return initDataSourceCheckOpen;
	}

	public void setInitDataSourceCheckOpen(boolean initDataSourceCheckOpen) {
		this.initDataSourceCheckOpen = initDataSourceCheckOpen;
	}

	public boolean isCheckThreadOpen() {
		return checkThreadOpen;
	}

	public void setCheckThreadOpen(boolean checkThreadOpen) {
		this.checkThreadOpen = checkThreadOpen;
	}

	public List<String> getWarnWeixins() {
		return warnWeixins;
	}

	public void setWarnWeixins(List<String> warnWeixins) {
		this.warnWeixins = warnWeixins;
	}

	public List<String> getWarnEmails() {
		return warnEmails;
	}

	public void setWarnEmails(List<String> warnEmails) {
		this.warnEmails = warnEmails;
	}

	public int getErrorTimes() {
		return errorTimes;
	}

	public void setErrorTimes(int errorTimes) {
		this.errorTimes = errorTimes;
	}

	public int getSuccessTimes() {
		return successTimes;
	}

	protected static ThreadPoolExecutor threadPoolExecutor;

	public void setSuccessTimes(int successTimes) {
		this.successTimes = successTimes;
	}

	/**
	 * 启动检测线程池
	 */
	protected static void initThreadPool() {
		threadPoolExecutor = new ThreadPoolExecutor(4, // 核心线程数
				10, // 最大线程数
				15, // 空闲线程 存活最大时间
				TimeUnit.SECONDS, // 单位秒
				new LinkedBlockingQueue<Runnable>(), // 没有长度限制的队列
				new ThreadPoolExecutor.CallerRunsPolicy()// 重试添加当前任务,会重复调用execute();
		);
	}

	// protected volatile int templateIndex = 0;

	protected AtomicInteger jdaoTemplateIndex = new AtomicInteger(0);
	protected AtomicInteger hdaoTemplateIndex = new AtomicInteger(0);

	protected AtomicInteger jdaoSize = new AtomicInteger(0);
	protected AtomicInteger hdaoSize = new AtomicInteger(0);

	// protected volatile int size = 0;

	protected long checkInterval = 2000; // 检测时间 2s

	public long getCheckInterval() {
		return checkInterval;
	}

	public void setCheckInterval(long checkInterval) {
		this.checkInterval = checkInterval;
	}

	/** hibenateTemplate容器 **/
	protected CopyOnWriteArrayList<HibernateTemplate> hibernateTemplates = new CopyOnWriteArrayList<HibernateTemplate>();

	/** jdbcTemplate容器 **/
	protected CopyOnWriteArrayList<JdbcTemplate> jdbcTemplates = new CopyOnWriteArrayList<JdbcTemplate>();

	public abstract DataSourceType getDataSourceType();

	@Override
	public <T> T getTemplate(TemplateType templateType) throws Exception {
		if (templateType == null)
			throw new Exception("SlaveTemplateContainer TemplateType can not be null!   -----cobra");
		if (hibernateTemplates == null || hibernateTemplates.size() == 0) {
			throw new Exception("SlaveTemplateContainer hibernateTemplates can not be empty!   -----cobra");
		}
		if (jdbcTemplates == null || jdbcTemplates.size() == 0) {
			throw new Exception("SlaveTemplateContainer jdbcTemplate can not be empty!   -----cobra");
		}
		if (jdaoSize.get() <= 0) {
			throw new Exception("SlaveTemplateContainer container size can not be <= 0!   -----cobra");
		}
		if (hdaoSize.get() <= 0) {
			throw new Exception("SlaveTemplateContainer container size can not be <= 0!   -----cobra");
		}
		if (jdaoTemplateIndex.get() < 0) {
			throw new Exception("SlaveTemplateContainer container jdaoTemplateIndex can not < 0!   -----cobra");
		}
		if (hdaoTemplateIndex.get() < 0) {
			throw new Exception("SlaveTemplateContainer container hdaoTemplateIndex can not < 0!   -----cobra");
		}

		int hashValue = hash(templateType);
		if (hashValue == -1)
			return null;
		if (TemplateType.HibernageTemplate.equals(templateType)) {
			return (T) hibernateTemplates.get(hashValue);
		} else if (TemplateType.JdbcTemplate.equals(templateType)) {
			return (T) jdbcTemplates.get(hashValue);
		} else
			return null;
	}

	/**
	 * 初始化 注意：master默认每次只加载一个，需要把要用的master数据源配置到最顶端
	 * 
	 * @param isMaster
	 *            是否是master
	 * @throws Exception
	 **/
	public boolean init(boolean isMaster) throws Exception {
		synchronized (AbstractTemplateContainer.class) {
			try {
				String prefix = "";
				if (isMaster) {
					prefix = "master";
				} else {
					prefix = "slave";
				}
				if (threadPoolExecutor != null) {
					if (!threadPoolExecutor.isShutdown()) {
						threadPoolExecutor.shutdown();
					}
					try {
						if (!threadPoolExecutor.isShutdown()) {
							threadPoolExecutor.shutdownNow();
						}
					} catch (Exception e) {
						e.printStackTrace();
					}
				}
				hibernateTemplates.clear();
				jdbcTemplates.clear();

				/** 初始化jdbctemplates **/
				Map<String, JdbcTemplate> jdaos = SpringContextUtil.getBeans(JdbcTemplate.class);
				int jdaoValidCount = 0;
				for (Map.Entry<String, JdbcTemplate> entity : jdaos.entrySet()) {
					if (entity.getValue() != null && entity.getKey() != null && entity.getKey().startsWith(prefix)) {
						// TODO 检测一下数据源情况
						boolean add = false;
						if (isMaster) {// master
							try {
								add = checkMasterStatusOnce(entity.getValue());
							} catch (Exception e) {
								e.printStackTrace();
								add = false;
							}
							if (add) {// 检测无问题，添加到有效区最后一个
								jdbcTemplates.add(jdaoValidCount, entity.getValue());
								jdaoValidCount++;
								break;// 只加载一个master
							} else {// 检测有问题，添加到末尾
								String subject = "检测master启动时出现问题，请检查";
								String content = "检测master启动时出现问题，服务已经出现严重问题，请检查并重启服务";
								sendWarnNotice(subject, content);
								logger.info(content);
								continue;
								// jdbcTemplates.add(jdbcTemplates.size(),
								// entity.getValue());
							}
						} else {// slave
							try {
								add = checkSlaveStatusOnce(entity.getValue());
							} catch (Exception e) {
								e.printStackTrace();
								add = false;
							}

							if (add) {// 检测无问题，添加到有效区最后一个
								jdbcTemplates.add(jdaoValidCount, entity.getValue());
								jdaoValidCount++;
							} else {// 检测有问题，添加到末尾
								jdbcTemplates.add(jdbcTemplates.size(), entity.getValue());
							}
						}
					}
				}
				jdaoSize.set(jdaoValidCount); // 初始化jdbctemplate容器有效区大小

				/** 初始化hibernateTemplates **/
				Map<String, HibernateTemplate> hdaos = SpringContextUtil.getBeans(HibernateTemplate.class);
				int hdaoValidCount = 0;
				for (Map.Entry<String, HibernateTemplate> entity : hdaos.entrySet()) {
					if (entity.getValue() != null && entity.getKey() != null && entity.getKey().startsWith(prefix)) {
						boolean add = false;
						if (isMaster) {// master
							try {
								add = checkMasterStatusOnce(entity.getValue());
							} catch (Exception e) {
								e.printStackTrace();
								add = false;
							}
							// add = checkMasterStatusOnce(entity.getValue());
							if (add) {// 检测无问题，添加到有效区最后一个
								hibernateTemplates.add(hdaoValidCount, entity.getValue());
								hdaoValidCount++;
								break;// 只加载一个master
							} else {// 检测有问题，添加到末尾
									// hibernateTemplates.add(hibernateTemplates.size(),
									// entity.getValue());
								String subject = "检测master启动时出现问题，请检查";
								String content = "检测master启动时出现问题，服务已经出现严重问题，请检查并重启服务";
								sendWarnNotice(subject, content);
								logger.info(content);
								continue;
							}
						} else {// slave
							try {
								add = checkSlaveStatusOnce(entity.getValue());
							} catch (Exception e) {
								e.printStackTrace();
								add = false;
							}
							if (add) {// 检测无问题，添加到有效区最后一个
								hibernateTemplates.add(hdaoValidCount, entity.getValue());
								hdaoValidCount++;
							} else {// 检测有问题，添加到末尾
								hibernateTemplates.add(hibernateTemplates.size(), entity.getValue());
							}
						}
					}
				}
				hdaoSize.set(hdaoValidCount); // 初始化hibernatetemplate容器有效区大小

				/** 启动检查 **/
				if (hibernateTemplates.size() != jdbcTemplates.size()) {
					String subject = "数据源容器初始化警告：数据源个数不相等";
					String content = "hibernateTemplates: " + hibernateTemplates.size() + "  ,  jdbcTemplates:" + jdbcTemplates.size() + "  请检查确认";
					sendWarnNotice(subject, content);
					logger.info(content);
					// throw new
					// Exception("AbstractTemplateContainer container init : hibernateTemplates.size() != jdbcTemplates.size()!   -----cobra");
				}
				initThreadPool();
				if (this.isCheckThreadOpen())
					check();
			} catch (Exception e) {
				e.printStackTrace();
				String subject = "数据容器初始化异常";
				String content = "数据容器初始化异常 " + e.getMessage();
				sendWarnNotice(subject, content);
				return false;
			}
		}
		return true;
	}

	/** jdbc检测slave **/
	protected boolean checkSlaveStatusOnce(JdbcTemplate jdao) throws Exception {
		if (!this.isInitDataSourceCheckOpen())
			return true;
		// jdao = getTemplate(TemplateType.JdbcTemplate);
		if (jdao == null) {
			return false;
		}
		Map<String, Object> status = jdao.queryForMap("show slave status");
		if (status == null || status.isEmpty()) {
			return false;
		}
		String Slave_IO_Running = (String) status.get("Slave_IO_Running");
		String Slave_SQL_Running = (String) status.get("Slave_SQL_Running");
		if (!"Yes".equalsIgnoreCase(Slave_IO_Running) || !"Yes".equalsIgnoreCase(Slave_SQL_Running)) {
			return false;
		}
		return true;
	}

	/** hibernate检测slave **/
	protected boolean checkSlaveStatusOnce(HibernateTemplate hdao) throws Exception {
		if (!this.isInitDataSourceCheckOpen())
			return true;
		if (hdao == null) {
			return false;
		}
		List<Object[]> list = (List<Object[]>) hdao.execute(new HibernateCallback<Object>() {
			@Override
			public Object doInHibernate(Session session) throws HibernateException, SQLException {
				return session.createSQLQuery("show slave status").list();
			}
		});
		if (list == null || list.size() == 0)
			return false;
		Object[] os = list.get(0);
		String Slave_IO_Running = (String) os[10];
		String Slave_SQL_Running = (String) os[11];
		if (!"Yes".equalsIgnoreCase(Slave_IO_Running) || !"Yes".equalsIgnoreCase(Slave_SQL_Running)) {
			return false;
		}
		return true;
	}

	/** jdbc检测master **/
	protected boolean checkMasterStatusOnce(JdbcTemplate jdao) throws Exception {
		if (!this.isInitDataSourceCheckOpen())
			return true;
		// jdao = getTemplate(TemplateType.JdbcTemplate);
		if (jdao == null) {
			return false;
		}
		Map<String, Object> status = jdao.queryForMap("show master status");
		if (status == null || status.isEmpty()) {
			return false;
		}
		return true;
	}

	/** hibernate检测master **/
	protected boolean checkMasterStatusOnce(HibernateTemplate hdao) throws Exception {
		if (!this.isInitDataSourceCheckOpen())
			return true;
		if (hdao == null) {
			return false;
		}
		List<Object[]> list = (List<Object[]>) hdao.execute(new HibernateCallback<Object>() {
			@Override
			public Object doInHibernate(Session session) throws HibernateException, SQLException {
				return session.createSQLQuery("show master status").list();
			}
		});
		if (list == null || list.size() == 0)
			return false;
		return true;
	}

	@Override
	public HibernateTemplate getHibernateTemplate() {
		try {
			return getTemplate(TemplateType.HibernageTemplate);
		} catch (Exception e) {
			e.printStackTrace();
			return null;
		}
	}

	@Override
	public JdbcTemplate getJdbcTemplate() {
		try {
			return getTemplate(TemplateType.JdbcTemplate);
		} catch (Exception e) {
			e.printStackTrace();
			return null;
		}
	}

	/** 发警告邮件、微信 **/
	public void sendWarnNotice(final String subject, final String content) {
		final List<String> emails = warnEmails;
		threadPoolExecutor.submit(new Runnable() {
			@Override
			public void run() {
				for (String toEmail : emails) {
					// TODO send email
				}
			}
		});

		final List<String> weixins = warnWeixins;
		threadPoolExecutor.submit(new Runnable() {
			@Override
			public void run() {
				for (String openId : weixins) {
					// TODO send weixin
				}
			}
		});
	}

	/** 抽取数据库信息 **/
	protected String extractDBInfo(JdbcTemplate jdao) {
		String content = "";
		try {
			DatabaseMetaData metaData = extractDBMetaData(jdao);
			if (metaData == null) {
				return content;
			} else {
				content += "<br/>数据库信息：<br/>";
				content += "连接url： " + metaData.getURL();
				content += "连接用户： " + metaData.getUserName();
			}
			return content;
		} catch (Exception e) {
			return content;
		}
	}

	// protected String extractDBInfo(HibernateTemplate hdao){
	// String content = "";
	// try {
	// DatabaseMetaData metaData = extractDBMetaData(hdao);
	// if(metaData == null){
	// return content;
	// }else{
	// content += "<br/>数据库信息：<br/>";
	// content += "连接url： " + metaData.getURL();
	// content += "连接用户： " + metaData.getUserName();
	// }
	// return content;
	// } catch (Exception e) {
	// return content;
	// }
	// }

	/** 抽取数据库连接URL **/
	protected String extractDBUrl(DatabaseMetaData metaData) {
		try {
			return metaData.getURL();
		} catch (Exception e) {
			return "";
		}
	}

	/** 抽取数据库连接用户 **/
	protected String extractDBUser(DatabaseMetaData metaData) {
		try {
			return metaData.getUserName();
		} catch (Exception e) {
			return "";
		}
	}

	/** 抽取DatabaseMetaData **/
	protected DatabaseMetaData extractDBMetaData(JdbcTemplate jdao) {
		try {
			if (jdao == null) {
				logger.info("jdao为空");
				return null;
			}
			DataSource dataSource = jdao.getDataSource();
			if (dataSource == null) {
				logger.info("dataSource为空");
				return null;
			}
			Connection conn = dataSource.getConnection();
			if (conn == null) {
				logger.info("conn为空");
				return null;
			}
			DatabaseMetaData metaData = conn.getMetaData();
			if (metaData == null) {
				logger.info("metaData为空");
				return null;
			} else {
				return metaData;
			}
		} catch (Exception e) {
			return null;
		}
	}

	// protected DatabaseMetaData extractDBMetaData(HibernateTemplate hdao){
	// try {
	// if(hdao == null){
	// logger.info("jdao为空");
	// return null;
	// }
	// DataSource dataSource = hdao.getSessionFactory().get.get.getDataSource();
	// if(dataSource == null){
	// logger.info("dataSource为空");
	// return null;
	// }
	// Connection conn = dataSource.getConnection();
	// if(conn == null){
	// logger.info("conn为空");
	// return null;
	// }
	// DatabaseMetaData metaData = conn.getMetaData();
	// if(metaData == null){
	// logger.info("metaData为空");
	// return null;
	// }else{
	// return metaData;
	// }
	// } catch (Exception e) {
	// return null;
	// }
	// }

	/** 抽取DataSource **/
	protected DataSource extractDataSource(JdbcTemplate jdao) {
		try {
			if (jdao == null) {
				logger.info("jdao为空");
				return null;
			}
			DataSource dataSource = jdao.getDataSource();
			if (dataSource == null) {
				logger.info("dataSource为空");
				return null;
			}
			return dataSource;
		} catch (Exception e) {
			return null;
		}
	}
}
